package com.anjubao.parking.municipal.roadside.service;

import com.anjubao.parking.municipal.common.dto.ApiResult;
import com.anjubao.parking.municipal.common.exception.ServiceException;
import com.anjubao.parking.municipal.common.utils.U;
import com.anjubao.parking.municipal.parkinglot.dto.EntryEnterDTO;
import com.anjubao.parking.municipal.parkinglot.dto.EntryLeaveDTO;
import com.anjubao.parking.municipal.parkinglot.dto.EntryResDTO;
import com.anjubao.parking.municipal.parkinglot.dto.ParkingTotalFeeDto;
import com.anjubao.parking.municipal.roadside.common.Constants;
import com.anjubao.parking.municipal.roadside.dto.*;
import com.anjubao.parking.municipal.roadside.feign.ParkingLotFeignClient;
import com.anjubao.parking.municipal.roadside.feign.ParkingRecordFeignClient;
import com.anjubao.parking.municipal.roadside.form.InParkingRecordForm;
import com.anjubao.parking.municipal.roadside.mapper.*;
import com.anjubao.parking.municipal.roadside.model.*;
import com.anjubao.parking.municipal.roadside.utils.DateUtils;
import com.anjubao.parking.municipal.roadside.utils.HttpUtils;
import com.anjubao.parking.municipal.roadside.utils.ParkingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class ParkingRecordService {

    private static final Logger logger = LoggerFactory.getLogger(ParkingRecordService.class);
    @Autowired
    private ParkingRecordMapper parkingRecordMapper;
    @Autowired
    private ParkingLotSlotMapper parkingLotSlotMapper;
    @Autowired
    private ParkingImageMapper parkingImageMapper;
    @Autowired
    private PayRecordMapper payRecordMapper;
    @Autowired
    private ParkingUtils parkingUtils;
    @Autowired
    private ParkingRecordFeignClient parkingRecordFeignClient;
    @Autowired
    private ParkingLotFeignClient parkingLotFeignClient;
    @Autowired
    private ThreadPoolTaskExecutor executor;
    @Autowired
    private HttpUtils httpUtils;

    @Value("${upload_image_url}")
    private String uploadImageUrl;
    @Value("${imgPrefix}")
    private String imgPrefix;
    @Value("${car_url}")
    private String carUrl;
    @Value("${car_out_wechat_url}")
    private String carOutWechatUrl;

    /**
     * 车辆进场
     *
     * @param inForm
     * @return
     */
    @Transactional
    public String inParkRecord(InParkingRecordForm inForm) {
        LotAndSlotDto lotSlot=parkingLotSlotMapper.getByCodeAndNum(inForm.getParkingLotCode(),inForm.getSlotNum());
        if (lotSlot==null) throw new ServiceException("无该车位，车辆进场失败！");
        //判断车位是否有车
        ParkingRecord parkingRecord=parkingRecordMapper.getByLotIdAndNum(lotSlot.getParkingLotId(),inForm.getSlotNum());
        if (parkingRecord != null ) {
            if (DateUtils.getDateStr(parkingRecord.getInTime()).equals(inForm.getInTime())){
                logger.info("【重收进场消息】该车位已经有车,该条进场消息时间与上条进场消息时间一样,该消息属于重发消息,不做处理！");
                return parkingRecord.getId();
            }else {
                logger.info("【特殊情况】该车位已经有车,车场id：{},车位编号：{},原停车记录id:{}",
                        lotSlot.getParkingLotId(),inForm.getSlotNum(),parkingRecord.getId());
                if (DateUtils.string2Date(inForm.getInTime()).before(parkingRecord.getInTime())){
                    throw new ServiceException("【日期错误】,后车进场时间比前车进场时间早！");
                }
                this.outParkRecord(inForm.getInTime(),parkingRecord.getId(),Constants.car_out_unnormal);
            }
        }
        //添加停车记录
        String recordId=String.valueOf(U.id());
        ParkingRecord parkingRecordNew = new ParkingRecord();
        parkingRecordNew.setId(String.valueOf(recordId));
        parkingRecordNew.setParkingType(1);
        parkingRecordNew.setParkingLotId(lotSlot.getParkingLotId());
        parkingRecordNew.setParkingLotCode(inForm.getParkingLotCode());
        parkingRecordNew.setParkingLotName(lotSlot.getParkingLotName());
        parkingRecordNew.setParkingLotAddress(lotSlot.getParkingLotAddress());
        parkingRecordNew.setParkingLotSlotCode(lotSlot.getParkingLotSlotCode());
        parkingRecordNew.setSlotNum(inForm.getSlotNum());
        parkingRecordNew.setInTime(DateUtils.string2Date(inForm.getInTime()));
        parkingRecordMapper.insert(parkingRecordNew);
        parkingLotSlotMapper.updateBylotIdAndNum(recordId,lotSlot.getParkingLotId(),inForm.getSlotNum());
        //推送车辆进场消息至智慧停车平台
        executor.submit(()->{
            EntryEnterDTO entryEnterDTO=parkingUtils.packCarInMessage(inForm, lotSlot.getParkingLotSlotCode(), recordId);
            ApiResult<EntryResDTO> apiResult = parkingRecordFeignClient.enter(entryEnterDTO);
            logger.info(" 推送入场消息至智慧平台{}, 车场code：{},车位编号：{},停车记录id:{}",apiResult.getCode() != 2000?"失败":"成功",
                    inForm.getParkingLotCode(),inForm.getSlotNum(),entryEnterDTO.getLocalRecordId());
        });
        return recordId;
    }

    /**
     * 车辆出场
     * @param outTime
     * @param parkingRecordId
     * @param type
     */
    @Transactional
    public void outParkRecord(String outTime,String parkingRecordId,int type) {
        ParkingRecord parkingRecord=parkingRecordMapper.selectById(parkingRecordId);
        if (type==Constants.car_out_normal){
            //判断车辆是否进场或者已出场
            if (parkingRecord==null){
                logger.info("无该车辆进场记录,程序不做任何处理,返回,停车记录id:{}",parkingRecordId);
                return;
            }
            if(parkingRecord!=null && !DateUtils.getDateStr(parkingRecord.getOutTime()).equals("1970-01-01 00:00:00")){
                logger.info("该停车记录已经出场,程序不做任何处理,返回,停车记录id:{}",parkingRecordId);
                return;
            }
            if (DateUtils.string2Date(outTime).before(parkingRecord.getInTime())){
                throw new ServiceException("【日期错误】,出场时间比进场时间早！");
            }
        }
        //计算是否欠费
        PayRecord prePayRecord = payRecordMapper.getPrePayRecord(parkingRecord.getId());
        PayRecord owePayRecord=parkingUtils.checkPay(outTime, parkingRecord, prePayRecord);
        if (owePayRecord != null) {//有欠费
            payRecordMapper.insert(owePayRecord);
            parkingRecord.setFeeState(2);
        }else {
            parkingRecord.setFeeState(3);
        }
        //更新停车记录表
        if (type==Constants.car_out_normal) {
            parkingRecord.setState(1);
        }else {
            parkingRecord.setState(2);
        }
        parkingRecord.setOutTime(DateUtils.string2Date(outTime));
        parkingRecordMapper.updateById(parkingRecord);
        parkingLotSlotMapper.resetParkingLotSlot("", parkingRecord.getId());
        //发送车辆出场消息至智慧停车平台
        executor.submit(()->{
            EntryLeaveDTO entryLeaveDTO=parkingUtils.packCarOutMessage(parkingRecord, prePayRecord,owePayRecord);
            ApiResult<EntryResDTO> apiResult = parkingRecordFeignClient.leave(entryLeaveDTO);
            logger.info("{}推送出场消息至智慧平台操作{},车场code：{},车位编号：{},停车记录id:{}",
                    type==Constants.car_out_normal?"":"【特殊情况】",apiResult.getCode() != 2000?"失败":"成功",
                    parkingRecord.getParkingLotCode(),parkingRecord.getSlotNum(),parkingRecord.getId());

        });
        //发送消息至公众号
        if (!StringUtils.isEmpty(parkingRecord.getUserId())){
            executor.submit(()->{
                CarOutWechatMessageDto messageDto=new CarOutWechatMessageDto();
                messageDto.setTitle("车辆出场").setName("安居宝路边停车").setProgress("出场").setRemark("当前时间:"+outTime);
                httpUtils.postForEntity(carOutWechatUrl+parkingRecord.getUserId(),messageDto);
                logger.info("{}推送出场消息至微信公众号成功,车场code：{},车位编号：{},停车记录id:{}",
                        type==Constants.car_out_normal?"":"【特殊情况】",
                        parkingRecord.getParkingLotCode(),parkingRecord.getSlotNum(),parkingRecord.getId());

            });
        }
    }

    /**
     * 平台获取停车记录列表
     * @param recordType  0：全部数据 1:未离场数据  2:已离场数据
     * @param parkingIllegal
     * @param page
     * @param pageSize
     * @return
     */
    public List<RdParkingRecordVo> listParkingRecord(Integer recordType, Integer parkingIllegal, int page, int pageSize) {
        Map<String, Object> map = new HashMap<>();
        if (recordType != null) map.put("recordType", recordType);
        map.put("page", page);
        map.put("pageSize", pageSize);
        List<ParkingPayRecordDto> recordVos=parkingRecordMapper.listParkingRecord(map);
        //批量获取停车费用
        List<ParkingTotalFeeDto> feeReqDtos=new ArrayList<>();
        for (ParkingPayRecordDto recordDto:recordVos) {
            if (recordDto.getFeeState()==0 || recordDto.getFeeState()==1){
                ParkingTotalFeeDto feeDto=new ParkingTotalFeeDto();
                feeDto.setParkingLotCode(recordDto.getParkingLotCode());
                feeDto.setParkingRecordId(recordDto.getParkingRecordId());
                feeDto.setEnterTime(DateUtils.getDateStr(recordDto.getInTime()));
                feeDto.setLeaveTime(DateUtils.getDateStr(new Date()));
                feeReqDtos.add(feeDto);
            }
        }
        List<ParkingTotalFeeDto>  feeResDtos=parkingLotFeignClient.getParkingTotalFeeBatch(feeReqDtos).getResult();
        List<RdParkingRecordVo> rdParkingRecordVos=new ArrayList<>();
        int sumPayed=0;//已缴费用
        int costAmount=0;//实际消费费用
        for (ParkingPayRecordDto recordDto:recordVos) {
            //低杆
            if (recordDto.getParkingType()==2){
                if (recordDto.getFeeState()==1){//进行中
                    sumPayed=0;
                    List<ParkingTotalFeeDto> feeDtos=feeResDtos.stream().
                            filter(i->i.getParkingRecordId().equals(recordDto.getParkingRecordId())).
                            collect(Collectors.toList());
                    costAmount=feeDtos.get(0).getTotalFee();

                }
                if (recordDto.getFeeState()==2 && recordDto.getPayRecordDtos()!=null){//待缴
                    sumPayed=0;
                    costAmount=recordDto.getPayRecordDtos().get(0).getAmount();
                }
                if (recordDto.getFeeState()==3  && recordDto.getPayRecordDtos()!=null){//已完成
                    costAmount=recordDto.getPayRecordDtos().get(0).getAmount();
                    sumPayed=costAmount;
                }
            }
            //中杆
            if (recordDto.getParkingType()==1){
                if (recordDto.getFeeState()==0 || recordDto.getFeeState()==1){//0未缴费，1：进行中
                    List<ParkingTotalFeeDto> feeDtos=feeResDtos.stream().
                            filter(i->i.getParkingRecordId().equals(recordDto.getParkingRecordId())).
                            collect(Collectors.toList());
                    costAmount=feeDtos.get(0).getTotalFee();
                    if (recordDto.getFeeState()==0)sumPayed=0;
                    if (recordDto.getFeeState()==1){
                        sumPayed=recordDto.getPayRecordDtos().stream().filter(i->i.getPayState()).
                        collect(Collectors.summingInt(PayRecordDto::getAmount));
                        if (recordDto.getEnableMonitor()==1){//开启哨兵
                            int parkingTime = (int)(new Date().getTime()-recordDto.getInTime().getTime()) /1000;
                            int monitorFee=((parkingTime - 1)/Constants.monitor_time_section + 1) * Constants.monitor_Fee_section;
                            costAmount=costAmount+monitorFee;
                        }
                    }
                }
                if (recordDto.getFeeState()==2){//待缴费
                    sumPayed=recordDto.getPayRecordDtos().stream().filter(i->i.getPayState()).
                            collect(Collectors.summingInt(PayRecordDto::getAmount));
                    costAmount=recordDto.getPayRecordDtos().stream().
                            collect(Collectors.summingInt(PayRecordDto::getAmount));
                }
                if (recordDto.getFeeState()==3){//已完成
                    sumPayed=recordDto.getPayRecordDtos().stream().filter(i->i.getPayState()).
                            collect(Collectors.summingInt(PayRecordDto::getAmount));
                    costAmount=sumPayed;

                }
            }
            RdParkingRecordVo recordVo=new RdParkingRecordVo();
            recordVo.setParkingRecordId(recordDto.getParkingRecordId());
            recordVo.setParkingName(recordDto.getParkingName());
            recordVo.setSlotNum(recordDto.getSlotNum());
            recordVo.setAddress(recordDto.getAddress());
            recordVo.setEnableMonitor(recordDto.getEnableMonitor());
            recordVo.setInTime(recordDto.getInTime());
            recordVo.setOutTime(recordDto.getOutTime());
            recordVo.setCostAmount(costAmount);
            recordVo.setTotalAmount(sumPayed);
            rdParkingRecordVos.add(recordVo);
        }
        return rdParkingRecordVos;
    }

    public int count(Integer recordType, Integer parkingIllegal) {
        Map<String, Object> map = new HashMap<>();
        if (parkingIllegal != null) {
            map.put("parkingIllegal", parkingIllegal);
        }
        if (recordType != null) {
            map.put("recordType", recordType);
        }
        return parkingRecordMapper.count(map);
    }

    /**
     * 获取停车记录图片
     * @param parkingRecordId
     * @param type 1-进场图片，2-出场图片，3-哨兵图片
     * @return
     */
    public List<String> listImages(String parkingRecordId, int type) {
        List<String> picList=parkingImageMapper.getPic(imgPrefix,parkingRecordId,type);
        return picList;
    }
}
